package com.example.myproject.service.impl;

import java.io.*;
import java.net.InetAddress;
import java.net.URI;
import java.util.*;
import java.util.stream.Collectors;

import com.example.myproject.dto.bounty.BountyContributionDTO;
import com.example.myproject.dto.bounty.BountyDTO;
import com.example.myproject.exception.BusinessException;
import com.example.myproject.mapper.TorrentMapper;
import com.example.myproject.mapper.TorrentTagMapper;
import com.example.myproject.mapper.UserMapper;
import com.example.myproject.model.*;
import com.example.myproject.repository.BountyContributionRepository;
import com.example.myproject.repository.BountyRepository;
import com.example.myproject.repository.BountySubmissionRepository;
import com.example.myproject.repository.BountyTagRepository;
import com.example.myproject.service.BountyService;
import com.example.myproject.service.UserMoneyService;
import com.example.myproject.tracker.TrackerManager;
import com.example.myproject.ttorrent.common.src.main.java.com.turn.ttorrent.common.TorrentCreator;
import com.example.myproject.vo.ApiResponse;
import com.example.myproject.vo.BountyContributionWithBountyVO;
import com.example.myproject.vo.BountyWithSubmissionVO;
import com.turn.ttorrent.bcodec.BDecoder;
import com.turn.ttorrent.bcodec.BEValue;
import com.turn.ttorrent.common.TorrentMetadata;
import com.turn.ttorrent.common.TorrentParser;
import com.turn.ttorrent.common.TorrentSerializer;
import lombok.AllArgsConstructor;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;

@Service
@AllArgsConstructor
public class BountyServiceImpl implements BountyService {

    private final BountyRepository bountyRepository;
    private final BountyContributionRepository bountyContributionRepository;
    private final BountySubmissionRepository bountySubmissionRepository;
    private final TorrentMapper torrentMapper;
    private final TorrentTagMapper torrentTagMapper;
    private final BountyTagRepository bountyTagRepository;
    private final UserMapper userMapper;
    private final TrackerManager trackerManager;
    private final UserMoneyService userMoneyService;

    @Override
    public Bounty publishBounty(Long userId, BountyDTO dto) {
        //先检查钱够不够，提前扣钱
        //暂时不收手续费
        // todo
        if (dto.getRewardAmount()<userMoneyService.getMagicValue(userId)){
            userMoneyService.reward( userId,0,0,0-dto.getRewardAmount());
        }else {
            throw new BusinessException("魔力值不够支付悬赏");
        }
        Bounty bounty = new Bounty();
        bounty.setCreatorId(userId);
        bounty.setBountyTitle(dto.getBountyTitle());
        bounty.setBountyDescription(dto.getBountyDescription());
        bounty.setCategoryId(dto.getCategoryId());
        bounty.setRewardAmount(dto.getRewardAmount());
        bounty.setTotalAmount(dto.getRewardAmount().intValue());
        bounty.setCreateTime(LocalDateTime.now());
        bounty.setDeadline(null);
        bounty.setBountyStatus("pending");
        bountyRepository.save(bounty);
        if(dto.getTags()!=null && dto.getTags().size()>0) {
            // 插入 bounty_tag 表
            Long bountyId = bounty.getBountyId();
            List<BountyTag> tagEntities = dto.getTags().stream()
                    .map(tagId -> new BountyTag(bountyId, tagId))
                    .collect(Collectors.toList());

            bountyTagRepository.saveAll(tagEntities);
        }
        return bountyRepository.save(bounty);
    }

    @Override
    public BountyContribution addContribution(Long userId,BountyContributionDTO dto) {
       //未受手续费
        if (dto.getContributedAmount()<userMoneyService.getMagicValue(userId)){
            userMoneyService.reward( userId,0,0,0-dto.getContributedAmount());
        }else {
            throw new BusinessException("魔力值不够支付悬赏");
        }
        BountyContribution contribution = new BountyContribution();
        contribution.setBountyId(dto.getBountyId());
        contribution.setContributorId(userId);
        contribution.setContributedAmount(dto.getContributedAmount());
        contribution.setContributedTime(LocalDateTime.now());
        return bountyContributionRepository.save(contribution);
    }
    //构造vo逻辑
    public List<BountyWithSubmissionVO> mapBountiesToVO(List<Bounty> bounties) {
        return bounties.stream().map(bounty -> {
            BountyWithSubmissionVO vo = new BountyWithSubmissionVO();
            vo.setBounty(bounty);

            Long submissionId = bounty.getSubmissionId();
            if (submissionId != null) {
                Optional<BountySubmission> submissionOpt = bountySubmissionRepository.findById(submissionId);
                submissionOpt.ifPresent(vo::setSubmission);
            }

            String creatorName = userMapper.selectUserNameById( bounty.getCreatorId());
            vo.setCreatorName(creatorName);

            return vo;
        }).collect(Collectors.toList());
    }

    @Override
    public List<BountyWithSubmissionVO> getMyBounties(Long userId) {
        List<Bounty> bountyList = bountyRepository.findAllByCreatorId(userId);
        return mapBountiesToVO(bountyList);
    }

    @Override
    public List<BountyWithSubmissionVO> getAllBounties() {
        List<Bounty> bountyList = bountyRepository.findAllByBountyStatus("pending"); // 只查进行中的悬赏
        return mapBountiesToVO(bountyList);
    }



    @Override
    public List<BountyContributionWithBountyVO> getMyContributions(Long userId) {
        List<BountyContribution> contributions = bountyContributionRepository.findAllByContributorId(userId);
        return contributions.stream().map(contribution -> {
            BountyContributionWithBountyVO vo = new BountyContributionWithBountyVO();
            vo.setContribution(contribution);
            bountyRepository.findById(contribution.getBountyId()).ifPresent(bounty -> {
                vo.setBounty(bounty);
                String creatorName = userMapper.selectUserNameById( bounty.getCreatorId());
                vo.setCreatorName(creatorName);
            });
            return vo;
        }).collect(Collectors.toList());
    }


    @Override
    public List<BountyWithSubmissionVO> getMySubmissions(Long userId) {
        List<BountySubmission> submissions = bountySubmissionRepository.findAllByResponserId(userId);
        return submissions.stream().map(submission -> {
            BountyWithSubmissionVO vo = new BountyWithSubmissionVO();
            vo.setSubmission(submission);
            bountyRepository.findById(submission.getBountyId()).ifPresent(bounty -> {
                vo.setBounty(bounty);
                String creatorName = userMapper.selectUserNameById(bounty.getCreatorId());
                vo.setCreatorName(creatorName);
            });
            return vo;
        }).collect(Collectors.toList());
    }


    @Override
    public ApiResponse<Void> cancelBounty(Long bountyId, Long userId) {
        System.out.println("用户：" + userId + " 取消悬赏：" + bountyId);
        Optional<Bounty> optional = bountyRepository.findById(bountyId);
        if (optional.isEmpty()) {
            return ApiResponse.error(404, "悬赏不存在");
        }

        Bounty bounty = optional.get();
        if (!bounty.getCreatorId().equals(userId)) {
            return ApiResponse.error(403, "无权限取消该悬赏");
        }

        bounty.setBountyStatus("cancel");
        bountyRepository.save(bounty);

        //退还赏金
        userMoneyService.reward(userId, 0, 0, bounty.getRewardAmount());
        // 同时追加的所有赏金也要退还
        List<BountyContribution> contributions = bountyContributionRepository.findByBountyId(bountyId);
        for (BountyContribution contribution : contributions) {
            Long contributorId = contribution.getContributorId();
            int amount = contribution.getContributedAmount();
            userMoneyService.reward(contributorId, 0, 0, amount);
        }
        return ApiResponse.success(200, "已成功取消悬赏");
    }

    @Override
    public ApiResponse<Void> approveSubmission(Long submissionId, Long userId) {
        Optional<BountySubmission> optional = bountySubmissionRepository.findById(submissionId);
        if (optional.isEmpty()) {
            return ApiResponse.error(400, "提交记录不存在");
        }

        BountySubmission submission = optional.get();
        Optional<Bounty> bountyOpt = bountyRepository.findById(submission.getBountyId());
        if (bountyOpt.isEmpty() || !bountyOpt.get().getCreatorId().equals(userId)) {
            return ApiResponse.error(400,"无权审批该悬赏提交");
        }
        Bounty bounty = bountyOpt.get();
        // 修改状态
        submission.setSubmissionStatus(BountySubmission.SubmissionStatus.approved);
        bounty.setBountyStatus("approved");

        //给响应悬赏的人发钱
        userMoneyService.reward(submission.getResponserId(), 0, 0, bounty.getTotalAmount());
        // 保存修改
        torrentMapper.updateStatusById(submission.getTorrentId(), "normal");
        bountySubmissionRepository.save(submission);
        bountyRepository.save(bounty);

        return ApiResponse.success(200, "操作成功");
    }

    @Override
    public ApiResponse<Void> rejectSubmission(Long submissionId, String reason, Long userId) {
        Optional<BountySubmission> optional = bountySubmissionRepository.findById(submissionId);
        if (optional.isEmpty()) {
            return ApiResponse.error(400,"提交记录不存在");
        }

        BountySubmission submission = optional.get();
        Optional<Bounty> bountyOpt = bountyRepository.findById(submission.getBountyId());
        if (bountyOpt.isEmpty() || !bountyOpt.get().getCreatorId().equals(userId)) {
            return ApiResponse.error(400,"无权拒绝该悬赏提交");
        }
        Bounty bounty = bountyOpt.get();

        submission.setSubmissionStatus(BountySubmission.SubmissionStatus.rejected);
        submission.setRefuseReason(reason);
        bounty.setBountyStatus("under_review");

        bountySubmissionRepository.save(submission);
        bountyRepository.save(bounty);
        return ApiResponse.success(200,"已成功提起申诉，申诉理由是：" + reason);

    }


    public long getTorrentFileSize(File torrentFile) throws Exception {
        Map<String, BEValue> torrent = BDecoder.bdecode(new FileInputStream(torrentFile)).getMap();
        Map<String, BEValue> info = torrent.get("info").getMap();

        if (info.containsKey("length")) {
            // 单文件
            return info.get("length").getLong();
        } else if (info.containsKey("files")) {
            // 多文件
            List<BEValue> files = info.get("files").getList();
            long total = 0;
            for (BEValue f : files) {
                total += f.getMap().get("length").getLong();
            }
            return total;
        } else {
            return 0;
        }
    }

    public static void createTorrentFile(String outputPath,
                                         String sourcePath,
                                         List<String> announceUrls,
                                         int pieceLengthKB) throws Exception {
        File source = new File(sourcePath);
        if (!source.exists() || !source.canRead()) {
            throw new IllegalArgumentException("无法访问源文件/目录: " + source.getName());
        }

        // 将 announce URL 字符串转换为 URI 对象
        List<URI> announceURIs = new ArrayList<>();
        for (String url : announceUrls) {
            announceURIs.add(new URI(url));
        }

        // 创建 announceList（支持多层 tracker）
        List<List<URI>> announceList = new ArrayList<>();
        announceList.add(announceURIs);  // 所有 tracker 都作为第一级（Tier 1）

        // 设置创建者信息
        String creator = String.format("%s (ttorrent)", System.getProperty("user.name"));
        int pieceLengthBytes = pieceLengthKB * 1024;

        // 创建 TorrentMetadata 对象
        TorrentMetadata torrent;
        if (source.isDirectory()) {
            List<File> files = new ArrayList<>(FileUtils.listFiles(source, TrueFileFilter.TRUE, TrueFileFilter.TRUE));
            Collections.sort(files);  // 保证一致性
            torrent = TorrentCreator.create(source, files, announceURIs.get(0), announceList, creator, pieceLengthBytes);
        } else {
            torrent = TorrentCreator.create(source, null, announceURIs.get(0), announceList, creator, pieceLengthBytes);
        }

        // 序列化并写入 .torrent 文件
        try (OutputStream fos = new FileOutputStream(outputPath)) {
            fos.write(new TorrentSerializer().serialize(torrent));
        }

//       System.out.println("Info hash: " + torrent.getHexInfoHash());

        System.out.println("Torrent file created at: " + outputPath);
    }

    @Override
    public Long submitFile(Long userId, Long bountyId, MultipartFile file) {
        // 使用当前工作目录 + 相对路径
        String baseUploadPath = System.getProperty("user.dir") + "/files/";
        String baseTorrentPath = System.getProperty("user.dir") + "/torrents/";

        String originalFilename = file.getOriginalFilename();
        if (originalFilename == null || originalFilename.isEmpty()) {
            throw new IllegalArgumentException("上传文件名不能为空");
        }

        // 创建目录（如不存在）
        File uploadDir = new File(baseUploadPath);
        if (!uploadDir.exists()) {
            boolean created = uploadDir.mkdirs();
            System.out.println("上传目录创建：" + created);
        }

        String uploadedFilePath = baseUploadPath + originalFilename;
        System.out.println("保存路径：" + uploadedFilePath);

        File uploadedFile = new File(uploadedFilePath);
        System.out.println("绝对路径：" + uploadedFile.getAbsolutePath());

        try {
            file.transferTo(uploadedFile);
        } catch (IOException e) {
            throw new BusinessException("保存资源文件失败");
        }


        System.out.println("原始文件已保存到: " + uploadedFilePath);


        // 生成 .torrent 文件
        String torrentFilename = originalFilename + ".torrent";
        String torrentFilePath = baseTorrentPath + torrentFilename;
        try {
            String localIP = InetAddress.getLocalHost().getHostAddress();
            System.out.println("本机地址IP: " + localIP);
            List<String> announceUrls = List.of("http://" + localIP + ":6969/announce");
            createTorrentFile(torrentFilePath, uploadedFilePath, announceUrls, 64);
        } catch (Exception e) {
            throw new BusinessException("生成种子文件失败");
        }


        File torrentFile = new File(torrentFilePath);
        trackerManager.registerTorrent(torrentFile);
        System.out.println("悬赏原始种子已生成并注册: " + torrentFilePath);

        // 入库
        Torrent t = new Torrent();
        System.out.println("before:"+bountyId);
        // 修改查找是不是有这个bounty
        if (bountyId != null) {
            Bounty bounty = bountyRepository.findById(bountyId)
                    .orElseThrow(() -> new BusinessException("悬赏不存在"));
            bounty.setBountyStatus("unconfirmed");
            bountyRepository.save(bounty);
            System.out.println(bounty.getBountyId()+"aaaaaaaaaaaaaaa1"+bounty.getBountyStatus());
           // System.out.println("bountyId is :"+bountyId);
            //添加种子的信息
            t.setTorrentName(bounty.getBountyTitle());
            t.setUploaderId(userId);
            t.setTorrentDescription(bounty.getBountyDescription());
            t.setTorrentSize(uploadedFile.length());
            t.setUploadTime(LocalDateTime.now());
            t.setTorrentStatus("bounty");
            t.setOriginPrice(0.00);
            t.setDownloadCount(0);
            t.setDownloadLimit(1000);//可能需要修改
            t.setScore(0.0);
            t.setTorrentPath(torrentFilePath);
            torrentMapper.insert(t);

            //生产完种子后，添加submission
            // 添加 bounty_submission 记录
            BountySubmission submission = new BountySubmission();
            submission.setBountyId(bountyId);
            submission.setResponserId(userId);
            submission.setTorrentId(t.getTorrentId());
            submission.setSubmitTime(LocalDateTime.now());
            submission.setSubmissionStatus(BountySubmission.SubmissionStatus.valueOf("unconfirmed"));
            submission.setAmountStatus(false);
            bountySubmissionRepository.save(submission);
            //修改bounty，把submissionid加进去
            bounty.setSubmissionId(submission.getSubmissionId());
            bounty.setBountyStatus("under_review");
            userMoneyService.reward(userId, 0, 0, bounty.getRewardAmount());
            bountyRepository.save(bounty);
            //修改torrent——tag表
            List<BountyTag> bountyTags = bountyTagRepository.findAllByBountyId(bountyId);

            for (BountyTag bt : bountyTags) {
                TorrentTag tt = new TorrentTag();
                tt.setTorrentId(t.getTorrentId());
                tt.setTagId(bt.getTagId());
                torrentTagMapper.insert(tt.getTorrentId(), bt.getTagId());
            }
        }
        return t.getTorrentId();
    }


    @Override
    public Long submitTorrent(Long userId, Long bountyId,
                              MultipartFile file) {
        String baseTorrentPath = System.getProperty("user.dir") + "/torrents/";

        String originalFilename = file.getOriginalFilename();
        if (originalFilename == null || !originalFilename.endsWith(".torrent")) {
            throw new IllegalArgumentException("请上传合法的 .torrent 文件");
        }

// 创建 torrents 目录（如不存在）
        File torrentDir = new File(baseTorrentPath);
        if (!torrentDir.exists()) {
            boolean created = torrentDir.mkdirs();
            System.out.println("种子目录创建：" + created);
        }

        File torrentFile = new File(baseTorrentPath + originalFilename);
        System.out.println("保存种子路径：" + torrentFile.getAbsolutePath());

        try {
            file.transferTo(torrentFile);
        } catch (IOException e) {
            throw new BusinessException("保存 .torrent 文件失败");
        }

        try {
            TorrentMetadata meta = new TorrentParser().parseFromFile(torrentFile);
            String announce = meta.getAnnounce().toString();
            if (announce.contains("passkey=")) {
                throw new BusinessException("请上传不含 passkey 的种子");
            }
        } catch (Exception e) {
            throw new BusinessException("解析种子文件失败: " + e.getMessage());
        }

        trackerManager.registerTorrent(torrentFile);

        // 入库
        Torrent t = new Torrent();
        // 修改查找是不是有这个bounty
        if (bountyId != null) {
            Bounty bounty = bountyRepository.findById(bountyId)
                    .orElseThrow(() -> new BusinessException("悬赏不存在"));
            bounty.setBountyStatus("unconfirmed");
            bountyRepository.save(bounty);

            //添加种子的信息
            t.setTorrentName(bounty.getBountyTitle());
            t.setUploaderId(userId);
            t.setTorrentDescription(bounty.getBountyDescription());
            //上传真实文件大小
            try {
                t.setTorrentSize(getTorrentFileSize(torrentFile));
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            t.setUploadTime(LocalDateTime.now());
            t.setTorrentStatus("bounty");
            t.setOriginPrice(0.00);
            t.setDownloadCount(0);
            t.setDownloadLimit(10000);//todo 需要修改
            t.setScore(0.0);
            t.setTorrentPath(torrentFile.getAbsolutePath());

            torrentMapper.insert(t);

            //生产完种子后，添加submission
            // 添加 bounty_submission 记录
            BountySubmission submission = new BountySubmission();
            submission.setBountyId(bountyId);
            submission.setResponserId(userId);
            submission.setTorrentId(t.getTorrentId());
            submission.setSubmitTime(LocalDateTime.now());
            submission.setSubmissionStatus(BountySubmission.SubmissionStatus.valueOf("unconfirmed"));
            submission.setAmountStatus(false);
            bountySubmissionRepository.save(submission);

            //修改bounty，把submissionid加进去
            bounty.setSubmissionId(submission.getSubmissionId());
            bounty.setBountyStatus("under_review");
            bountyRepository.save(bounty);

            //修改torrent——tag表
            List<BountyTag> bountyTags = bountyTagRepository.findAllByBountyId(bountyId);

            for (BountyTag bt : bountyTags) {
                TorrentTag tt = new TorrentTag();
                tt.setTorrentId(t.getTorrentId());
                tt.setTagId(bt.getTagId());
                torrentTagMapper.insert(tt.getTorrentId(), bt.getTagId());
            }
        }
        return t.getTorrentId();
    }


    //获取仲裁列表
    @Override
    public List<BountyWithSubmissionVO> getSubmissionsForArbitration() {
        // 1. 查询所有处于仲裁状态的 bounty
        List<Bounty> bountyList = bountyRepository.findAllByBountyStatus("under_review");

        return bountyList.stream().map(bounty -> {
            BountyWithSubmissionVO vo = new BountyWithSubmissionVO();
            vo.setBounty(bounty);

            // 2. 获取该 bounty 对应的 submission（你可以根据需要只查一个，或查所有）
            List<BountySubmission> submissions = bountySubmissionRepository.findAllByBountyId(bounty.getBountyId());
            if (!submissions.isEmpty()) {
                // 假设只取最新的或第一个
                vo.setSubmission(submissions.get(0));
            }

            // 3. 获取发布人昵称
            User user = userMapper.selectById(bounty.getCreatorId());
            if (user != null) {
                vo.setCreatorName(user.getUserName());
            }

            return vo;
        }).collect(Collectors.toList());
    }


    //仲裁通过，即响应悬赏的种子不符合要求
    @Override
    public ApiResponse<Void> approveByArbitration(Long submissionId) {
        Optional<BountySubmission> opt = bountySubmissionRepository.findById(submissionId);
        if (opt.isEmpty()) return ApiResponse.error(400, "提交记录不存在");

        BountySubmission submission = opt.get();

        Optional<Bounty> bountyOpt = bountyRepository.findById(submission.getBountyId());
        if (bountyOpt.isEmpty()) return ApiResponse.error(400, "悬赏不存在");

        Bounty bounty = bountyOpt.get();

        // 修改状态
        submission.setSubmissionStatus(BountySubmission.SubmissionStatus.rejected);
        bounty.setBountyStatus("rejected");
        torrentMapper.updateStatusById(submission.getTorrentId(), "violated");
        bountySubmissionRepository.save(submission);
        bountyRepository.save(bounty);

        //退还赏金
        userMoneyService.reward(bounty.getCreatorId(), 0, 0, bounty.getRewardAmount());
        // 同时追加的所有赏金也要退还
        List<BountyContribution> contributions = bountyContributionRepository.findByBountyId(bounty.getBountyId());
        for (BountyContribution contribution : contributions) {
            Long contributorId = contribution.getContributorId();
            int amount = contribution.getContributedAmount();
            userMoneyService.reward(contributorId, 0, 0, amount);
        }
        return ApiResponse.success(200, "同意仲裁，种子无效，将归还赏金");
    }


    //拒绝仲裁
    @Override
    public ApiResponse<Void> rejectByArbitration(Long submissionId) {
        Optional<BountySubmission> opt = bountySubmissionRepository.findById(submissionId);
        if (opt.isEmpty()) return ApiResponse.error(400, "提交记录不存在");

        BountySubmission submission = opt.get();

        Optional<Bounty> bountyOpt = bountyRepository.findById(submission.getBountyId());
        if (bountyOpt.isEmpty()) return ApiResponse.error(400, "悬赏不存在");

        Bounty bounty = bountyOpt.get();

        submission.setSubmissionStatus(BountySubmission.SubmissionStatus.approved);
        bounty.setBountyStatus("approved");
        //给响应悬赏的人发钱
        userMoneyService.reward(submission.getResponserId(), 0, 0, bounty.getTotalAmount());

        torrentMapper.updateStatusById(submission.getTorrentId(), "normal");
        bountySubmissionRepository.save(submission);
        bountyRepository.save(bounty);

        return ApiResponse.success(200, "拒绝仲裁，种子合法");
    }
}
